package org.sample.springbootcommandlinerunner.web;

import org.sample.springbootcommandlinerunner.web.invocation.InvocationException;
import org.sample.springbootcommandlinerunner.web.invocation.InvocationRequest;
import org.sample.springbootcommandlinerunner.web.invocation.InvocationResponse;
import org.sample.springbootcommandlinerunner.web.model.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.time.Duration;
import java.time.temporal.ChronoUnit;

@Service
public class WebConnService {

	private static final Logger LOG = LoggerFactory.getLogger(WebConnService.class);

	private static final String AUTHORIZATION_HTTP_HEADER_NAME = "authorization";

	private static final String SESSION_HTTP_HEADER_NAME = "session";

	private static final String AUTHORIZATION_HTTP_HEADER_VALUE = "";

	private static final String CONNECTOR_BASE_URL = "http://localhost:3011";

	private static final String CREATE_CHAT_SESSION_V30_URL = "/web/api/3.0/client/chats/sessions";

	private static final String ROUTER_CALLBACK_V30_URL = "/web/api/3.0/cxbot-router/callback";

	private static final String CREATE_LIVECHAT_SESSION_V30_URL = "/web/api/3.0/client/livechat/sessions";

	private static final String END_LIVECHAT_SESSION_V30_URL = "/web/api/3.0/client/livechat/end";

	private static final String CREATE_CHAT_SESSION_V31_URL = "/web/api/3.1/client/chats/sessions";

	private static final String CREATE_LIVECHAT_SESSION_V31_URL = "/web/api/3.1/client/livechat/sessions";

	private static final String CREATE_CHAT_SESSION_V40_URL = "/web/api/4.0/client/chats/sessions";

	private static final String GET_QUEUE_RESULT_V40_URL = "/web/api/4.0/client/queue";

	private static final String CREATE_LIVECHAT_SESSION_V40_URL = "/web/api/4.0/client/livechat/sessions";

	@Autowired
	private WebClient webClient;

	public Mono<InvocationResponse<CreateChatResponse>> createChatV30(
			InvocationRequest<CreateChatRequest> request) {

		URI apiUri = UriComponentsBuilder.fromHttpUrl(CONNECTOR_BASE_URL)
				.path(CREATE_CHAT_SESSION_V30_URL)
				.build().toUri();

		return webClient.post().uri(apiUri)
				.headers(headers -> headers.add(AUTHORIZATION_HTTP_HEADER_NAME, AUTHORIZATION_HTTP_HEADER_VALUE))
				.bodyValue(request.body())
				.exchangeToMono(clientResponse ->
						acceptResponse(clientResponse, request.serialNumber(), CreateChatResponse.class));
	}

	public Mono<InvocationResponse<RouterCallbackResponse>> routerCallbackV30(
			InvocationRequest<RouterCallbackRequest> request) {

		URI apiUri = UriComponentsBuilder.fromHttpUrl(CONNECTOR_BASE_URL)
				.path(ROUTER_CALLBACK_V30_URL)
				.build().toUri();

		return webClient.post().uri(apiUri)
				.headers(headers -> headers.add(AUTHORIZATION_HTTP_HEADER_NAME, AUTHORIZATION_HTTP_HEADER_VALUE))
				.bodyValue(request.body())
				.exchangeToMono(clientResponse ->
						acceptResponse(clientResponse, request.serialNumber(), RouterCallbackResponse.class));
	}

	public Mono<InvocationResponse<StartLiveChatResponse>> startLiveChatV30(
			InvocationRequest<StartLiveChatRequest> request) {

		URI apiUri = UriComponentsBuilder.fromHttpUrl(CONNECTOR_BASE_URL)
				.path(CREATE_LIVECHAT_SESSION_V30_URL)
				.build().toUri();

		return webClient.post().uri(apiUri)
				.headers(headers -> headers.add(AUTHORIZATION_HTTP_HEADER_NAME, AUTHORIZATION_HTTP_HEADER_VALUE))
				.bodyValue(request.body())
				.exchangeToMono(clientResponse ->
						acceptResponse(clientResponse, request.serialNumber(), StartLiveChatResponse.class));
	}

	public Mono<InvocationResponse<Boolean>> endLiveChatV30(InvocationRequest<Void> request) {

		URI apiUri = UriComponentsBuilder.fromHttpUrl(CONNECTOR_BASE_URL)
				.path(END_LIVECHAT_SESSION_V30_URL)
				.build().toUri();

		return webClient.delete().uri(apiUri)
				.headers(headers -> {
					headers.add(AUTHORIZATION_HTTP_HEADER_NAME, AUTHORIZATION_HTTP_HEADER_VALUE);
					headers.add(SESSION_HTTP_HEADER_NAME, request.headers().get("session"));
				})
				.exchangeToMono(clientResponse ->
						acceptResponse(clientResponse, request.serialNumber(), Boolean.class));
	}

	public Mono<InvocationResponse<CreateChatResponse>> createChatV31(
			InvocationRequest<CreateChatRequest> request) {

		URI apiUri = UriComponentsBuilder.fromHttpUrl(CONNECTOR_BASE_URL)
				.path(CREATE_CHAT_SESSION_V31_URL)
				.build().toUri();

		return webClient.post().uri(apiUri)
				.headers(headers -> headers.add(AUTHORIZATION_HTTP_HEADER_NAME, AUTHORIZATION_HTTP_HEADER_VALUE))
				.bodyValue(request.body())
				.exchangeToMono(clientResponse ->
						acceptResponse(clientResponse, request.serialNumber(), CreateChatResponse.class));
	}

	public Mono<InvocationResponse<StartLiveChatResponse>> startLiveChatV31(
			InvocationRequest<StartLiveChatRequest> request) {

		URI apiUri = UriComponentsBuilder.fromHttpUrl(CONNECTOR_BASE_URL)
				.path(CREATE_LIVECHAT_SESSION_V31_URL)
				.build().toUri();

		return webClient.post().uri(apiUri)
				.headers(headers -> headers.add(AUTHORIZATION_HTTP_HEADER_NAME, AUTHORIZATION_HTTP_HEADER_VALUE))
				.bodyValue(request.body())
				.exchangeToMono(clientResponse ->
						acceptResponse(clientResponse, request.serialNumber(), StartLiveChatResponse.class));
	}

	public Mono<InvocationResponse<CreateChatResponse>> createChatV40(
			InvocationRequest<CreateChatRequest> request) {

		URI apiUri = UriComponentsBuilder.fromHttpUrl(CONNECTOR_BASE_URL)
				.path(CREATE_CHAT_SESSION_V40_URL)
				.build().toUri();

		return webClient.post().uri(apiUri)
				.headers(headers -> headers.add(AUTHORIZATION_HTTP_HEADER_NAME, AUTHORIZATION_HTTP_HEADER_VALUE))
				.bodyValue(request.body())
				.exchangeToMono(clientResponse ->
						acceptResponse(clientResponse, request.serialNumber(), CreateChatResponse.class));
	}

	public Mono<InvocationResponse<CreateChatResponse>> createChatV40Result(
			InvocationRequest<CreateChatRequest> request) {

		return Mono.just(request)
				.flatMap(req ->
						createChatV40ResultOnce(req)
								.delayElement(Duration.of(1, ChronoUnit.SECONDS)))
				.repeat()
				.takeUntil(response ->
					response.body().valid()
				)
				.last();
	}

	private Mono<InvocationResponse<CreateChatResponse>> createChatV40ResultOnce(
			InvocationRequest<CreateChatRequest> request) {

		URI apiUri = UriComponentsBuilder.fromHttpUrl(CONNECTOR_BASE_URL)
				.path(GET_QUEUE_RESULT_V40_URL)
				.pathSegment(request.pathVariables().get("sequence"))
				.build().toUri();

		return webClient.get().uri(apiUri)
				.headers(headers -> headers.add(AUTHORIZATION_HTTP_HEADER_NAME, AUTHORIZATION_HTTP_HEADER_VALUE))
				.exchangeToMono(clientResponse ->
						acceptResponse(clientResponse, request.serialNumber(), CreateChatResponse.class));
	}

	public Mono<InvocationResponse<StartLiveChatResponse>> startLiveChatV40(
			InvocationRequest<StartLiveChatRequest> request) {

		URI apiUri = UriComponentsBuilder.fromHttpUrl(CONNECTOR_BASE_URL)
				.path(CREATE_LIVECHAT_SESSION_V40_URL)
				.build().toUri();

		return webClient.post().uri(apiUri)
				.headers(headers -> headers.add(AUTHORIZATION_HTTP_HEADER_NAME, AUTHORIZATION_HTTP_HEADER_VALUE))
				.bodyValue(request.body())
				.exchangeToMono(clientResponse ->
						acceptResponse(clientResponse, request.serialNumber(), StartLiveChatResponse.class));
	}

	public Mono<InvocationResponse<StartLiveChatResponse>> startLiveChatV40Result(
			InvocationRequest<StartLiveChatRequest> request) {

		return Mono.just(request)
				.flatMap(req ->
						startLiveChatV40ResultOnce(req)
								.delayElement(Duration.of(2, ChronoUnit.SECONDS)))
				.repeat(60)
				.takeUntil(response ->
						response.body().valid()
				)
				.last();
	}

	private Mono<InvocationResponse<StartLiveChatResponse>> startLiveChatV40ResultOnce(
			InvocationRequest<StartLiveChatRequest> request) {

		URI apiUri = UriComponentsBuilder.fromHttpUrl(CONNECTOR_BASE_URL)
				.path(GET_QUEUE_RESULT_V40_URL)
				.pathSegment(request.pathVariables().get("sequence"))
				.build().toUri();

		return webClient.get().uri(apiUri)
				.headers(headers -> headers.add(AUTHORIZATION_HTTP_HEADER_NAME, AUTHORIZATION_HTTP_HEADER_VALUE))
				.exchangeToMono(clientResponse ->
						acceptResponse(clientResponse, request.serialNumber(), StartLiveChatResponse.class));
	}

	private <R> Mono<InvocationResponse<R>> acceptResponse(ClientResponse clientResponse, int serialNumber,
			Class<R> responseClass) {

		if (clientResponse.statusCode().is2xxSuccessful()) {
			return clientResponse.bodyToMono(responseClass)
					.map(response ->
						new InvocationResponse<R>().serialNumber(serialNumber).body(response)
					);
		} else if (clientResponse.statusCode().is4xxClientError() || clientResponse.statusCode().is5xxServerError()) {
			return clientResponse.bodyToMono(ErrorResponse.class)
					.flatMap(response ->
						Mono.error(new InvocationException().serialNumber(serialNumber).body(response))
					);
		} else {
			return clientResponse.createException().flatMap(ex ->
					Mono.error(new InvocationException().serialNumber(serialNumber).exception(ex)));
		}
	}


}
